﻿#include "MyScene.h"



int MyScene::counter = 0;

MyScene::MyScene()
	:NUM_GHOST		(4)
	,NUM_CHARACTERS	(1 + NUM_GHOST)//only 1 Pac-man, first element of mCharacterList
	,START_POSITION	(57)
	,START_POS_ENEMIES	(192)
	,DRAW_PER_LOGIC_STEP(10)
	,LOOPS_SPECIAL		(100)

	,mSpecialRemain		(0)
	,mEndGame			(NOT_ENDED)
{
	mBg = new Background();

	mCandiesList   = new CollectableItemsList(mBg);
	mInputCtrl	   = new InputController();

	mPlayer		   = new PacMan(mInputCtrl);
	mCharacterList = new Character*[NUM_CHARACTERS];

	mCharacterList[0] = new PacMan();
	for(int i = 1; i < NUM_CHARACTERS; i++)
	{
		mCharacterList[i] = new Ghost();
		//wait(2.0F);
	}

	mCollisionMgr = new CollisionManager();
	mRemainCandies = mCandiesList->numAllCandies();
	setupComponents();
}

MyScene::~MyScene(void)
{
	delete mCollisionMgr;
	for(int i = 0; i < NUM_CHARACTERS; i++)
	{
		delete mCharacterList[i];
	}
	delete [] mCharacterList;
	delete mPlayer;
	delete mInputCtrl;
	delete mCandiesList;
	delete mBg;
}

void MyScene::setupComponents() const
{
	setupCharCoord(START_POSITION, mPlayer);
	setupCharsCoord(START_POS_ENEMIES);
	setupCandiesCoord();
	
	setupCollisionMgr();
}

void MyScene::setupCharCoord(const int startIndex, Character* nChar, const bool inactive) const 
{//assuming we're counting only the walkable tiles
	int walkableIndex = 0, tileIndex;
	BackgroundElement* currentTile;

	for(tileIndex = 0; tileIndex < NUM_TILES; tileIndex++)
	{
		currentTile = mBg->getTile(tileIndex);
		if(currentTile->isWalkable())
		{
			if(walkableIndex == startIndex)
			{
				setCharacterPosition(nChar, tileIndex, currentTile);
				
				if(!inactive) nChar->activate();
				break;
			}
			++walkableIndex;
		}
	}
}

void MyScene::setupCharsCoord(const int centralIndex, const bool inactive) const
{
	int walkableIndex = 0,
		charIndex	  = 0,
		midTilesIndex;

	BackgroundElement* currentTile;
	for(int tileIndex = 0; tileIndex < NUM_TILES && tileIndex >= 0; ++tileIndex)
	{
		currentTile = mBg->getTile(tileIndex);
		if(currentTile->isWalkable())
		{
			if(walkableIndex == centralIndex) 
			{
				setCharacterPosition(mCharacterList[charIndex], tileIndex, currentTile);
				if(!inactive) mCharacterList[charIndex]->activate();
				++charIndex;

				midTilesIndex = tileIndex;
				break;
			}
			++walkableIndex;
		}
	}
	for(int k = 1; (midTilesIndex+k < NUM_TILES || midTilesIndex-k >= 0) && charIndex < NUM_CHARACTERS; ++k)
	{
		currentTile = mBg->getTile(midTilesIndex+k);
		if(currentTile->isWalkable())
		{
			setCharacterPosition(mCharacterList[charIndex], midTilesIndex+k, currentTile);	
			if(!inactive) mCharacterList[charIndex]->activate();
			++charIndex; 
		}
		if(charIndex < NUM_CHARACTERS){
			currentTile = mBg->getTile(midTilesIndex-k);
			if(currentTile->isWalkable())
			{
				setCharacterPosition(mCharacterList[charIndex], midTilesIndex-k, currentTile);
				if(!inactive) mCharacterList[charIndex]->activate();
				++charIndex;
			}
		}
	}
}

void MyScene::setupCharsCoord() const
{
	setupCharsCoord((NUM_TILES - mBg->getNumWall()) / 2);
}

void MyScene::setupCandiesCoord() const
{
	int numTilesWalkable   = NUM_TILES-mBg->getNumWall();
	int firstFreeTileIndex = NUM_TILES,
		lastFreeTileIndex  = -1;
	//if(mCandiesList->numAllCandies() > numTilesWalkable) ;//the candies exceed the possible location
	//else 
	if(mCandiesList->numAllCandies() < numTilesWalkable) 
	{//free from candies the central area of the stage
		int numFreeTiles   = numTilesWalkable - (mCandiesList->numAllCandies());
		firstFreeTileIndex = ( mCandiesList->numAllCandies() )/2;
		lastFreeTileIndex  = firstFreeTileIndex + numFreeTiles -1;//I don't know why without the -1 is letting 1 candy out
	}
	//else the amount of candies equals the number possible location

	int remainingCandies  = mCandiesList->numAllCandies(),
		candyIndex		  = 0,
		specialCandyIndex = 0;
	int walkableTileIndex = -1;

	bool topLeft, topRight, bottomLeft, bottomRight;
	topLeft = topRight = bottomLeft = bottomRight = false;
	const int NEIGHBORHOOD = 4; //specialCandies will be located near the corners in the neighborhood established here

	BackgroundElement* currentTile;

	for(int tileIndex = 0; tileIndex < NUM_TILES; tileIndex++)
	{
		currentTile = mBg->getTile(tileIndex);
		if(currentTile->isWalkable())
		//position candies only on walkable tiles
		{
			++walkableTileIndex;
			if(walkableTileIndex < firstFreeTileIndex || walkableTileIndex > lastFreeTileIndex)
			//except the tiles previously decided to stay empty
			{
			bool candyPlaced = false;
			
			if(!topLeft || !topRight || !bottomLeft || !bottomRight)
			//to position the special candies, one per corner of the stage
			{
				Candy* specialCandy = mCandiesList->mSpecialCandies[specialCandyIndex];
				int col = mBg->getCol(tileIndex),
					row = mBg->getRow(tileIndex);

				if( (!topLeft || !bottomLeft)
					&&
					col < NEIGHBORHOOD			)
				{
					if(!topLeft && row < NEIGHBORHOOD)
					{
						specialCandy->setCoord(currentTile->getXcoord(), currentTile->getYcoord());
						specialCandy->setTileIndex(tileIndex);
						specialCandy->activate();
						
						++specialCandyIndex;
						topLeft     = true;
						candyPlaced = true;
					}
					else if(!bottomLeft && mBg->tilesPerHeight()-row < NEIGHBORHOOD)
					{
						specialCandy->setCoord(currentTile->getXcoord(), currentTile->getYcoord());
						specialCandy->setTileIndex(tileIndex);
						specialCandy->activate();

						++specialCandyIndex;
						bottomLeft  = true;
						candyPlaced = true;
					}
				} else if( (!topRight || !bottomRight)
					&&
					mBg->tilesPerBase()-col < NEIGHBORHOOD )
				{
					if(!topRight && row < NEIGHBORHOOD)
					{
						specialCandy->setCoord(currentTile->getXcoord(), currentTile->getYcoord());
						specialCandy->setTileIndex(tileIndex);
						specialCandy->activate();

						++specialCandyIndex;
						topRight    = true;
						candyPlaced = true;
					}
					else if(!bottomRight && mBg->tilesPerHeight()-row < NEIGHBORHOOD)
					{
						specialCandy->setCoord(currentTile->getXcoord(), currentTile->getYcoord());
						specialCandy->setTileIndex(tileIndex);
						specialCandy->activate();

						++specialCandyIndex;
						bottomRight = true;
						candyPlaced = true;
					}
				}
				
			}

			if (!candyPlaced && candyIndex < mCandiesList->numCandies())
			//if(! placed a specialCandy) position a normal one
			{
				Candy* myCandy = mCandiesList->mCandies[candyIndex];

				myCandy->setCoord(currentTile->getXcoord(), currentTile->getYcoord());
				myCandy->setTileIndex(tileIndex);
				myCandy->activate();

				++candyIndex;
				candyPlaced = true;
			}
			
			if(candyPlaced) 
				if(--remainingCandies <= 0) 
					break;
			}
		}

	}
}

void MyScene::setupCollisionMgr() const
{
	mCollisionMgr->copyToStaticObjArray(mCandiesList);
	//mCollisionMgr->copyToStaticObjArray(reinterpret_cast<GameEntity**>(mCandiesList->mCandies), mCandiesList->numCandies());
	GameEntity** tmpCharList = reinterpret_cast<GameEntity**>(mCharacterList);
	//I don't like the reinterpret_cast but  
	//		other casts doesn't work on this and
	//		I dislike more to specify that every DynamicObject must be a Character
	mCollisionMgr->copyToDynamicObjArray(tmpCharList, NUM_CHARACTERS, mPlayer);
}





void MyScene::DrawTo(Bitmap* renderTarget, const int x, const int y) const
{
	++counter;

	mBg->DrawTo(renderTarget, isSpecialActive(), x, y);
	mCandiesList->DrawTo(renderTarget, x, y);
	for(int i = 0, k = 1; i < NUM_CHARACTERS; i++)
	{
		mCharacterList[i]->DrawTo(renderTarget, isSpecialActive(), x, y);
		if(mCharacterList[i]->isAPacMan())
		{//Label on the side about the score and status of PCs'PacMans		//or it is PackMans?
			char* aiState = (mCharacterList[i]->isActive())? "^-^" : "+_+";
			renderTarget->RenderText(x+500, y+95+(20*k), aiState, 80, 225, 102);
			char str[80];
			scoreToString(mCharacterList[i], str);
			renderTarget->RenderText(x+500, y+110+(20*k), "PC", 80, 225, 102);
			renderTarget->RenderText(x+520, y+110+(20*k), str, 80, 225, 102);
			++k;
		}
	}

	mPlayer->DrawTo(renderTarget, x, y);

	if(mPlayer->isAPacMan()){
		//Label on the side about the player's score and lives status
		int lives = /*static_cast<PacMan*>*/(mPlayer)->getLives();
		char liv[20];
		char* heart = "<3";
		strcpy(liv, "");
		for(int l = 0; l < lives; ++l) strcat(liv, heart);
		renderTarget->RenderText(x+500, y+85, liv, 250, 75, 92);
		char str[80];
		scoreToString(mPlayer, str);
		renderTarget->RenderText(x+500, y+100, str, 250, 75, 92);
	}
}

void MyScene::updateLogic()
{
	if(counter % DRAW_PER_LOGIC_STEP == 0){
		//update game logic
		//mng specialEffect active
		
		
		if(mPlayer->isActive()){
			mPlayer->move(mBg);						
		} else{					
			if(mPlayer->updateRemainLoops() == 0)
			{
				setupCharCoord(START_POSITION, mPlayer, true);
				mPlayer->activate();	
			}
		}

		for(int i = 0; i < NUM_CHARACTERS; i++)
		{
			if(mCharacterList[i]->isActive()){
				mCharacterList[i]->move(mBg);				
			} else{
				if(mCharacterList[i]->updateRemainLoops() == 0){
					setupCharCoord(START_POS_ENEMIES+i, mCharacterList[i], true);
					mCharacterList[i]->activate();	
				}
			}
		}
		mCollisionMgr->update(this);

		if(mSpecialRemain > 0)		{ --mSpecialRemain;		}

		if(mPlayer->getLives() == 0){ mEndGame = GAME_OVER; }
		else {
			if(mRemainCandies == 0)	{
				for(int i = 0; i < NUM_CHARACTERS; ++i)
				{
					if(mCharacterList[i]->isAPacMan()){
						if(mPlayer->getScore() < static_cast<PacMan*>(mCharacterList[i])->getScore() ){
							mEndGame = LOST;
							break;
						}
					}
				} if(mEndGame != LOST){ mEndGame = WON; }
			}
		}
		counter = 0;
	}
}


//void MyScene::attach(InputController* inputCtrl)
//{
//	PacMan* pac = reinterpret_cast<PacMan*>(mCharacterList[0]);
//	if(pac != nullptr)
//		pac->attach(inputCtrl);
//}
//void MyScene::detach()
//{
//	PacMan* pac = reinterpret_cast<PacMan*>(mCharacterList[0]);
//	if(pac != nullptr)
//		pac->detach();
//}

const int MyScene::stageHeigth() const
{
	return mBg->tileDimension()*mBg->tilesPerHeight();
}
const int MyScene::stageWidth() const
{
	return mBg->tileDimension()*mBg->tilesPerBase();
}

void MyScene::keyInput(const unsigned int Key, const KeyStatus status)
{
	mInputCtrl->keyInput(Key, status);
}

void MyScene::setCharacterPosition(Character* nChar, const int nTileIndex, BackgroundElement* nTile) const
{
	nChar->setCoord(nTile->getXcoord(), nTile->getYcoord());
	nChar->setTileIndex(nTileIndex);
	nChar->setCurrTile(nTile);
	nChar->setupProximity(mBg, nTile);//or tileIndex?
}

void MyScene::scoreToString(Character* nChar, char* oStr) const
{
	char scoreInt[24];
	int sc = static_cast<PacMan*>(nChar)->getScore();
	_itoa_s(sc, scoreInt, 10);

	strcpy(oStr, "Score: ");
	strcat(oStr, scoreInt);
}

void MyScene::setToSpecial()
{
	mSpecialRemain = LOOPS_SPECIAL;
}